package shaw.wifi.nohttp;

import java.io.StringWriter;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import shaw.wifi.NetworkSettingsTaskManager;
import android.util.Log;

public class SWEncrypt {	
	private PublicKey serverRSAKey;
	
	private byte[] sharedKey;
	private SecretKeySpec sharedKeySpec;
	private byte[] ivBytes;
	private IvParameterSpec sharedIv;
	
	public boolean setServerRSAKey(byte[] e, byte[] m) {
		BigInteger mod = new BigInteger(m);
		BigInteger exp = new BigInteger(e);
		
		Log.e("CRYPT", "got mod/exp " + mod.toString() + " " + exp.toString());
		
		RSAPublicKeySpec spec = new RSAPublicKeySpec(mod, exp);
		try {
			KeyFactory keyFact = KeyFactory.getInstance("RSA", "BC");
			serverRSAKey = keyFact.generatePublic(spec);
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Got RSA Key...");
			return true;
		} catch (NoSuchAlgorithmException Exc) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot get RSA key (NSA)...");
			Exc.printStackTrace();
			return false;
		} catch (InvalidKeySpecException Exc) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot generate RSA key (Invalid)...");
			Exc.printStackTrace();
			return false;
		} catch (NoSuchProviderException Exc) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("No support for BC...");
			Exc.printStackTrace();
			return false;
		}
	}
	
	public byte[] encryptRSA(byte[] input) {
		byte[] resultBytes = null;
		
		try {
			Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
			c.init(Cipher.ENCRYPT_MODE, serverRSAKey);
			Log.e("ENCR", c.getBlockSize() + "");
			return c.doFinal(input);
		
		} catch (InvalidKeyException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot encrypt message (invalid key)...");
		} catch (NoSuchAlgorithmException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot encrypt message (NSA)...");
		} catch (NoSuchPaddingException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot encrypt message (NSP)...");
		} catch (IllegalBlockSizeException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Message too large for key...");
		} catch (BadPaddingException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Bad message padding...");
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		}
		
		return resultBytes;
	}
	
	public void generateSharedKey() {
		//generate key
		byte[] key = new byte[32];
		StringWriter sw = new StringWriter();
		
		for(int i = 0; i<32; i++) {
			key[i] = (byte) ( Math.random() * ((double)Byte.MAX_VALUE*2) + ((double)Byte.MIN_VALUE) );
			sw.append(String.valueOf((int) key[i]));
		}
		
		Log.e("SHAREDSECRET", sw.toString());
		
		
		//generate IV
		byte[] iv = new byte[16];
		for(int i = 0; i<16; i++) {
			iv[i] = (byte) ( Math.random() * ((double)Byte.MAX_VALUE*2) + ((double)Byte.MIN_VALUE) );
		}
		ivBytes = iv;
		setSharedIv(iv);
		
		sharedKey = key;
		sharedKeySpec = new SecretKeySpec(key, "AES");
	}
	
	public byte[] getSharedKey() {
		return sharedKey;
	}
	
	public void setSharedIv(byte[] iv) {
		sharedIv = new IvParameterSpec(iv);
	}
	
	public byte[] encryptAES(byte[] input) {
		byte[] encrypted = null;
		
		try {
			Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
			c.init(Cipher.ENCRYPT_MODE, sharedKeySpec, sharedIv);
			encrypted = c.doFinal(input);
		} catch (NoSuchAlgorithmException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot encyrypt message (NSA)...");
		} catch (NoSuchPaddingException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot encyrypt message (NSP)...");
		} catch (InvalidKeyException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Invalid key...");
		} catch (InvalidAlgorithmParameterException e) {
			e.printStackTrace();
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Invalid algorithm parameter...");
		} catch (IllegalBlockSizeException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Block too large for key...");
		} catch (BadPaddingException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Bad message padding...");
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		}

		return encrypted;
	}
	
	public byte[] decryptAES(byte[] input) {
		byte[] decrypted = null;
		
		try {
			Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding", "BC");
			c.init(Cipher.DECRYPT_MODE, sharedKeySpec, sharedIv);
			decrypted = c.doFinal(input);
		} catch (NoSuchAlgorithmException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot encyrypt message (NSA)...");
		} catch (NoSuchPaddingException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Cannot encyrypt message (NSP)...");
		} catch (InvalidKeyException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Invalid key...");
		} catch (InvalidAlgorithmParameterException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Invalid algorithm parameter...");
		} catch (IllegalBlockSizeException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Block too large for key...");
			e.printStackTrace();
		} catch (BadPaddingException e) {
			NetworkSettingsTaskManager.get().getNSTR().getCurrentTask().outputMessage("Bad message padding...");
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		}

		return decrypted;
	}

	public byte[] getIv() {
		return ivBytes;
	}
}
